//////////////////////////////////////////////////////////////////////////
/// \file:  TextFile.h
/// Copyright (Tencent) 2007 - All Rights Reserved
/// Author: robetma.
/// Date:   [2008/06/26]
/// Description:
///     
//////////////////////////////////////////////////////////////////////////
#pragma once
#include <string>

typedef wchar_t FILENAMECHAR;


class CTextFileBase
{
public:
	enum TEXTENCODING
	{
		ASCII,
		UNI16_BE,
		UNI16_LE,
		UTF_8
	};

	CTextFileBase();
	~CTextFileBase();

	//	Is the file open?
	int IsOpen();

	//	Close the file
	virtual void Close();

	//	Return the encoding of the file (ASCII, UNI16_BE, UNI16_LE or UTF_8);
	TEXTENCODING GetEncoding() const;

	//	Set which character that should be used when converting
	//	Unicode->multi byte and an unknown character is found ('?' is default)
	void SetUnknownChar(const char unknown);

	//	Returns true if data was lost
	//	(happens when converting Unicode->multi byte string and an unmappable
	//	characters is found).
	bool IsDataLost() const;

	//	Reset the data lost flag
	void ResetDataLostFlag();

	//	Note!
	//	The codepage is only used when converting from multibyte
	//	to Unicode or vice versa. It is not used when reading
	//	ANSI-files in none-Unicode strings, or reading
	//	Unicode-files in Unicode strings.

	//	This means that if you want to read a ANSI-textfile
	//	(with some code page) to an non-Unicode string you
	//	must do the conversion yourself. But this is easy :-).
	//	Read the file with the codepage to a wstring, then use
	//	ConvertCharToWstring to convert the wstring to a string.

	//	Set codepage to use when working with none-Unicode strings
	void SetCodePage(const UINT codepage);

	//	Get codepage to use when working with none-Unicode strings
	UINT GetCodePage() const;

	//	Convert char* to wstring
	static void ConvertCharToWstring(const char* from, std::wstring &to,	UINT codepage = CP_ACP);

	//	Convert wchar_t* to string
	static void ConvertWcharToString(const wchar_t* from,
	                                 std::string &to,
									 UINT codepage = CP_ACP,
	                                 bool* datalost = NULL,
	                                 char unknownchar = 0);


protected:
	//	Convert char* to wstring
	void CharToWstring(const char* from, std::wstring& to) const;
	//	Convert wchar_t* to string
	void WcharToString(const wchar_t* from, std::string& to);

	//	The encoding of the file
	TEXTENCODING m_encoding;

	//	Buffer size
#define BUFFSIZE 1024

	HANDLE m_hFile;

	//	True if end of file
	bool m_bEndOfFile;
	//	Reading buffer
	char m_buf[BUFFSIZE];
	//	Buffer position
	int m_nBuffPos;
	//	Size of buffer
	int m_nBuffSize;

	//	Character used when converting Unicode->multi byte and an unknown character was found
	char m_unknownChar;

	//	Is true if data was lost when converting Unicode->multi-byte
	bool m_bDatalLost;

	UINT m_codepage;
};

class CTextFileWrite : public CTextFileBase
{
public:
	CTextFileWrite(const FILENAMECHAR* filename, TEXTENCODING type = ASCII, DWORD dwMethod = CREATE_ALWAYS);

	~CTextFileWrite();

	//	Write routines
	void Write(const char* text);
	void Write(const wchar_t* text);
	void Write(const std::string& text);
	void Write(const std::wstring& text);

	CTextFileWrite& operator << (const char c);
	CTextFileWrite& operator << (const char* text);
	CTextFileWrite& operator << (const std::string& text);

	CTextFileWrite& operator << (const wchar_t wc);
	CTextFileWrite& operator << (const wchar_t* text);
	CTextFileWrite& operator << (const std::wstring& text);

	//	Write new line (two characters, 13 and 10)
	void WriteEndl();

	//	Close the file
	virtual void Close();

private:
	//	Write and empty buffer
	void Flush();

	//	Write a single one wchar_t, convert first
	void WriteWchar(const wchar_t ch);

	//	Write one byte
	void WriteByte(const unsigned char byte);

	//	Write a c-string in ASCII-format
	void WriteAsciiString(const char* s);

	//	Write byte order mark
	void WriteBOM();
};


class CTextFileRead : public CTextFileBase
{
public:
	CTextFileRead(const FILENAMECHAR* filename);

	//	Returns false if end-of-file was reached
	//	(line will not be changed). If returns true,
	//	it means that last line ended with a line break.
	bool ReadLine(std::string& line);
	bool ReadLine(std::wstring& line);

	//	Returns everything from current position.
	bool Read(std::string& all, const std::string newline = "\r\n");
	bool Read(std::wstring& all, const std::wstring newline = L"\r\n");

	//	End of file?
	bool Eof() const;

private:
	//	Guess the number of characters in the file
	int GuessCharacterCount();

	//	Read line to wstring
	bool ReadWcharLine(std::wstring& line);

	//	Read line to string
	bool ReadCharLine(std::string& line);

	//	Reset the file pointer to start
	void ResetFilePointer();

	//	Read one wchar_t
	void ReadWchar(wchar_t& ch);

	//	Read one byte
	void ReadByte(unsigned char& ch);

	//	Detect encoding
	void ReadBOM();

	//	Use extra buffer. Sometimes we read one character to much, save it.
	bool m_bUseExtraBuffer;

	//	Used to read see if the first line in file is to read
	//	(so we know how to handle \n\r)
	bool m_bFirstLine;

	//	Extra buffer. It's ok to share the memory
	union
	{
		char m_extraBuffer_char;
		wchar_t m_extraBuffer_wchar;
	};
};


//	This is only used in Windows mode (no MFC)
//	An exception is thrown will data couldn't be read or written
class CTextFileException
{
public:
	CTextFileException(DWORD err)
		: m_errorCode(err)
	{}

	//	Value returned by GetLastError()
	DWORD m_errorCode;
};
